package functions

import (
	"fmt"
	"strings"
	"time"
)

const (
	layout = "20060102150405"
)

func layoutFormat(layout string) string {
	layout = strings.Replace(layout, "yyyy", "2006", 1)
	layout = strings.Replace(layout, "yy", "06", 1)
	layout = strings.Replace(layout, "MM", "01", 1)
	layout = strings.Replace(layout, "M", "1", 1)
	layout = strings.Replace(layout, "dd", "02", 1)
	layout = strings.Replace(layout, "d", "2", 1)
	layout = strings.Replace(layout, "HH", "15", 1)
	layout = strings.Replace(layout, "mm", "04", 1)
	layout = strings.Replace(layout, "ss", "05", 1)
	return layout
}

type datetime struct {
	layout string
	t      time.Time
}

func (d datetime) String() string {
	return d.t.Format(d.layout)
}

func (d datetime) Year() int {
	return d.t.Year()
}

func (d datetime) Month() int {
	return int(d.t.Month())
}

func (d datetime) Day() int {
	return d.t.Day()
}

func (d datetime) YearDay() int {
	return d.t.YearDay()
}

func (d datetime) WeekDay() int {
	return int(d.t.Weekday())
}

func (d datetime) AddHours(hours int) datetime {
	d.t = d.t.Add(time.Duration(hours) * time.Hour)
	return d
}

func (d datetime) AddDays(days int) datetime {
	d.t = d.t.AddDate(0, 0, days)
	return d
}

func (d datetime) AddMonths(months int) datetime {
	d.t = d.t.AddDate(0, months, 0)
	return d
}

func (d datetime) DiffHour(datetime string) (int, error) {
	end, err := time.ParseInLocation(d.layout, datetime, time.Local)
	if err != nil {
		return 0, fmt.Errorf("Datetime format error: %v", err)
	}
	return int(d.t.Sub(end).Hours()), nil
}

func (d datetime) DiffDay(datetime string) (int, error) {
	end, err := time.ParseInLocation(d.layout, datetime, time.Local)
	if err != nil {
		return 0, fmt.Errorf("Datetime format error: %v", err)
	}
	t1 := d.t.UTC().Truncate(24 * time.Hour)
	t2 := end.UTC().Truncate(24 * time.Hour)
	return int(t1.Sub(t2).Hours() / 24), nil
}

func AddDays(d *datetime, days int) datetime {
	d.t = d.t.AddDate(0, 0, days)
	return *d
}

func DiffDay(d *datetime, datetime string) int {
	end, err := time.ParseInLocation(d.layout, datetime, time.Local)
	if err != nil {
		throwException("Datetime format error: %v", err)
	}
	t1 := d.t.UTC().Truncate(24 * time.Hour)
	t2 := end.UTC().Truncate(24 * time.Hour)
	return int(t1.Sub(t2).Hours() / 24)
}

func Now() *datetime {
	return &datetime{
		layout: "2006-01-02 15:04:05",
		t:      time.Now(),
	}
}

func Curdate() *datetime {
	return &datetime{
		layout: "2006-01-02",
		t:      time.Now(),
	}
}

func Curtime() *datetime {
	return &datetime{
		layout: "15:04:05",
		t:      time.Now(),
	}
}

func Time(args ...string) (*datetime, error) {
	switch len(args) {
	case 0:
		return &datetime{
			layout: layout,
			t:      time.Now(),
		}, nil
	case 1:
		t, err := time.ParseInLocation(layout, args[0], time.Local)
		if err != nil {
			return nil, fmt.Errorf("Function parameter error: %v", err)
		}
		return &datetime{
			layout: layout,
			t:      t,
		}, nil
	}
	layout := layoutFormat(args[1])
	t, err := time.ParseInLocation(layout, args[0], time.Local)
	if err != nil {
		return nil, fmt.Errorf("Function parameter error: %v", err)
	}
	return &datetime{
		layout: layout,
		t:      t,
	}, nil
}
